home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Libraries / SeansWindowManager / SeansWindowManager.p < prev   
Encoding:
Text File  |  1994-05-05  |  16.1 KB  |  408 lines  |  [TEXT/R*ch]

  1. {©1994 by Sean Crist (kurisuto@chopin.udel.edu).  (Soon I'll have a new address on some machine}
  2. {in the domain upenn.edu; try reaching me there if chopin.udel.edu doesn't work.) I'd appreciate any comments or }
  3. {bug reports.  This code may be freely used for any purpose, except that military use is expressly prohibited.}
  4. {--------------------------------------------------------------------------------------------------}
  5.  
  6.  
  7. {The purpose of the following code may not be immediately apparent, but it can make life a LOT easier if you're}
  8. {writing an application which can have any number of open windows of multiple types (ResEdit is a good example}
  9. {of this kind of application).  }
  10.  
  11. {Basically, this code offers a way of putting a little ID collar around the neck of each window.  This way, you}
  12. {don't need to keep WindowPtr's to all your windows; it's like letting the dogs off their leashes.}
  13.  
  14. {The hard part is figuring out exactly how to use this unit, but if you've got the patience to do that, you're in good shape.}
  15.  
  16.  
  17. unit SeansWindowManager;
  18.  
  19. {     What this code is for:  This is basically an extension to the Window Manager 1) to support floating palettes, and}
  20. { 2) to eliminate the need to keep WindowPtr's to each window.  If you are writing a *simple* application with a fixed }
  21. {number of windows, you don't need this code; you can just define your WindowPtr's as globals.  But if your application }
  22. {supports arbitrary numbers of windows (e.g. a word processor which allows any number of documents to be open at a }
  23. {time), it can be a hassle to keep track of all of these WindowPtr's.  The following code makes it easy to manage large }
  24. {numbers of windows of assorted kinds. }
  25. {     The basic strategy of these routines is to put a handle in the RefCon of every window opened by your application }
  26. {(not only your 'document' windows but also your Clipboard window, your modeless dialogs, etc).  The structure of}
  27. {the data in this handle is defined below; it contains information uniquely identifying the window.  You don't need to keep}
  28. {any pointers to specific windows.}
  29. {     Of course, you need not be greatly concerned with these RefCon handles; just make the appropriate calls to the}
  30. {routines below, and most of the grubby work will be done for you.}
  31.  
  32. {There's several changes you must make to your code, and to the code below, for this all to work properly:}
  33. {1.  You must edit the list of wTypes below to reflect all the kinds of windows your program supports.  E.g. ResEdit}
  34. {      might have wClipboard = 1, wTEXTeditor = 2, wICONeditor = 3, etc. for all its kinds of windows.}
  35. {2.  You must edit the SetPrivateHilite routine below to reflect the relation between palettes and regular}
  36. {      windows in a way specific to your application.  For example, if you want your wToolsPalette to become}
  37. {      visible when one of your wArtWindows is selected, you should code this here.}
  38. {3.  Your program's initialization routine must set the PrivateFrontWindow global (declared below) to some}
  39. {      valid WindowPtr.  It doesn't matter which window this is, as long as it is a valid one.  The code below}
  40. {      will change the PrivateFrontWindow in response to your calls, but it needs a window to start out with.}
  41. {4.  Every time you open a new window, you must call InitializeWindow on it to record what kind of window it is.}
  42. {      Just before you dispose of a window, you must call DisposeWData on it first.}
  43. {5.  You must not ever call the Toolbox routine SelectWindow, since this will mess up the hiliting of windows and }
  44. {      visibility of palettes.  Use the routine PrivateActivate (defined below) in place of SelectWindow.}
  45. {6.   In your event loop, you need to do a little extra work to support floating palettes. We treat a window and }
  46. {      its palettes as one 'layer'.  When you get a mousedown in PrivateFrontWindow, or in one of the palettes}
  47. {      belonging to PrivateFrontWindow, then you should just call BringToFront on that window.}
  48. {      (Your event code thus needs to be smart about what kinds of palettes belong to what kinds }
  49. {      of windows.)}
  50. {      If the mousedown is in some other window (i.e. neither PrivateFrontWindow nor its palettes), then you should}
  51. {      call PrivateActivate, which is much like SelectWindow except that it does the extra work to support floating}
  52. {      palettes and make sure everything gets hilited properly.}
  53.  
  54. {I know this all sounds horribly complicated, but if you look over this code for a little, I think it will start to make}
  55. {sense.}
  56.  
  57.  
  58. interface
  59.  
  60. {Sample window types}
  61.     const
  62.         wMiscWindow = 1;
  63.         wMiscWindowWithPalette = 2;
  64.         wMiscPalette = 3;
  65. {These window types are just to illustrate how this code works.  In your program, you would want to change these}
  66. {constants to whatever kinds of windows your program supports.  (The application I'm working on has about 15}
  67. {different kinds of windows and three kinds of palettes, for example.)  Each window type, including palettes, must}
  68. {have a unique identifying number.}
  69.  
  70.     const
  71.         BadWindow = 9999;
  72. {For doing error checking.  We send this to our own error handlers, pretending it's a real Toolbox error code.}
  73.  
  74.     var
  75.         PrivateFrontWindow: WindowPtr;
  76. {This is the WindowPtr to the window that we _consider_ to be the front window, ignoring whatever floating palettes}
  77. {might be in front of it in the actual window list.  Note: you must initialize this to some valid WindowPtr when your}
  78. {program initializes itself, or this code will crash.}
  79.  
  80.  
  81.  
  82. {We store the following record as a handle in the RefCon field of a window.  It contains all of our information that}
  83. {we need to keep track of what kind of window this is.  Most of the time, you'll never need to manipulate the contents}
  84. {of this record directly, since there's more convenient routines below to do this work for you.}
  85.     type
  86.         WindowData = record
  87.                 Class: Integer;        {What kind of window or dialog?  This is one of the wType constants above.}
  88.                 Owner: Integer;
  89.                 Index: Integer;
  90.                 DataHandle: Handle;    {Handle to whatever other data you wish to store for this window.}
  91.                 AuxData: Integer;    {To be used however you like.}
  92.             end;
  93.         WDataPtr = ^WindowData;
  94.         WDataHandle = ^WDataPtr;
  95. {     Note on the fields 'Class', 'Owner', and 'Index':  the Class is the kind of window, e.g. a wClipboard window,}
  96. {a search-and-replace modeless dialog, or whatever.  This field contains one of the wType constants defined above.}
  97. {The owner and index tell which window of that kind we are talking about.  In the case where there's only one }
  98. {instance of a kind of window (such as a Clipboard window), you can just set Owner and Index to 0 when you call }
  99. {InitializeWindow.  When there's more than one instance of a particular window type, give each window a different }
  100. {Index to distinguish it.}
  101. {     Under normal circumstances, you can just set Owner to 0 in all cases.  I include this field because my application }
  102. {has script windows for different kinds of objects, so I need to know if this is, say, the script window for}
  103. {the 7th zone or the 7th room or whatever.}
  104.  
  105.  
  106.  
  107. {Call InitializeWindow when you are opening a new window.  You are responsible for creating the window with}
  108. {GetNewCWindow or GetNewDialog or whatever; pass this handle in WhichWindow.  InitializeWindow allocates}
  109. {a WDataHandle, installs it in the refCon of whichWindow, and sets up the Class, Owner, and Index values.}
  110.     function InitializeWindow (whichWindow: WindowPtr; Class, Owner, Index: Integer): Boolean;
  111.  
  112. {Dispose the WDataHandle associated with whichWindow.  Note:  You are responsible for disposing whatever}
  113. {data you have stored in DataHandle.  The right order to dispose things is: 1) dispose any data of your own}
  114. {which you've stored in DataHandle 2) call DisposeWData  3) dispose of the window or dialog itself with}
  115. {the appropriate Toolbox call.}
  116.     procedure DisposeWData (whichWindow: WindowPtr);
  117.  
  118. {Store this handle in DataHandle.}
  119.     procedure SetWHandle (whichWindow: WindowPtr; TheHandle: Handle);
  120.  
  121. {Return the handle stored in DataHandle.}
  122.     function GetWHandle (whichWindow: WindowPtr): Handle;
  123.  
  124. {Given a window, return its WDataHandle}
  125.     function GetWData (whichWindow: WindowPtr): WDataHandle;
  126.  
  127. {What is the class of whichWindow?  (You could accomplish this same thing by calling GetWData and then}
  128. {looking at MyWData^^.Class, but this routine saves work.)  Returns -1 if the WData is bad (i.e., you probably}
  129. {didn't call InitializeWindow on this window.}
  130.     function GetWClass (whichWindow: WindowPtr): Integer;
  131.  
  132. {What is the index of whichWindow?  (You could accomplish this same thing by calling GetWData and then}
  133. {looking at MyWData^^.Index, but this routine saves work.)  Returns -1 if the WData is bad (i.e., you probably}
  134. {didn't call InitializeWindow on this window.}
  135.     function GetWIndex (whichWindow: WindowPtr): Integer;
  136.  
  137. {Search through all the windows and try to find the one which has the specified class, owner, and index.}
  138.     function SearchWindow (Class, Owner, Index: Integer): WindowPtr;
  139.  
  140. {Same as SearchWindow, but for windows like the Clipboard which have only one instance.}
  141.     function FindUniqueWindow (Class: Integer): WindowPtr;
  142.  
  143. {Try to bring to front the window with this class, owner, and index.  Returns TRUE if there is no such window,}
  144. {i.e.  the window can't be activated and must be created.  This can make your code succinct; when the user}
  145. {does some action to open a particular window, your routine MyOpenWindow can start out 'if CantActivate}
  146. {(the window I need) then (do whatever to open it, including calling InitializeWindow)'.}
  147.     function CantActivate (Class, Owner, Index: Integer): Boolean;
  148.  
  149. {You should use this instead of SelectWindow.}
  150.     procedure PrivateActivate (whichWindow: WindowPtr);
  151.  
  152.  
  153. implementation
  154.  
  155.     procedure doOSErr (ErrorCode: Integer);
  156.     begin
  157. {Handle the error however you like.  I usually post an alert.}
  158.         Sysbeep(1);
  159.     end;
  160.  
  161. {Error checking.  Make sure the WDataHandle is a valid one by checking its length.  This is}
  162. {mainly for debugging.}
  163.     function IsValidWindowHandle (TheWDataHandle: WDataHandle): Boolean;
  164.         var
  165.             HandleSize: LongInt;
  166.     begin
  167.         IsValidWindowHandle := TRUE;
  168.         HandleSize := GetHandleSize(Handle(TheWDataHandle));
  169.         if HandleSize <> SizeOf(WindowData) then
  170.             IsValidWindowHandle := FALSE;
  171.     end;
  172.  
  173.  
  174.     function GetWData (whichWindow: WindowPtr): WDataHandle;
  175.         var
  176.             thePeek: WindowPeek;
  177.             TheHandle: Handle;
  178.     begin
  179.         if WhichWindow = nil then
  180.             begin
  181.                 doOSErr(BadWindow);
  182.                 GetWData := nil;
  183.             end
  184.         else
  185.             begin
  186.                 thePeek := WindowPeek(WhichWindow);
  187.                 TheHandle := Handle(thePeek^.RefCon);
  188.                 GetWData := WDataHandle(TheHandle);
  189.             end;
  190.     end;
  191.  
  192.  
  193.     function GetWClass (whichWindow: WindowPtr): Integer;
  194.         var
  195.             WData: WDataHandle;
  196.     begin
  197.         WData := GetWData(whichWindow);
  198.         if IsValidWindowHandle(WData) then
  199.             GetWClass := WData^^.Class
  200.         else
  201.             GetWClass := -1;
  202.     end;
  203.  
  204.  
  205.     function GetWIndex (whichWindow: WindowPtr): Integer;
  206.         var
  207.             WData: WDataHandle;
  208.     begin
  209.         WData := GetWData(whichWindow);
  210.         if IsValidWindowHandle(WData) then
  211.             GetWIndex := WData^^.Index
  212.         else
  213.             GetWIndex := -1;
  214.     end;
  215.  
  216.  
  217.     function GetWHandle (whichWindow: WindowPtr): Handle;
  218.         var
  219.             WData: WDataHandle;
  220.     begin
  221.         WData := GetWData(whichWindow);
  222.         if IsValidWindowHandle(WData) then
  223.             GetWHandle := WData^^.DataHandle
  224.         else
  225.             begin
  226.                 GetWHandle := nil;
  227.                 doOSErr(BadWindow);
  228.             end;
  229.     end;
  230.  
  231.  
  232.     procedure SetWHandle (whichWindow: WindowPtr; TheHandle: Handle);
  233.         var
  234.             WData: WDataHandle;
  235.     begin
  236.         WData := GetWData(whichWindow);
  237.         if IsValidWindowHandle(WData) then
  238.             WData^^.DataHandle := TheHandle
  239.         else
  240.             doOSErr(BadWindow);
  241.     end;
  242.  
  243.  
  244.  
  245.     function InitializeWindow (whichWindow: WindowPtr; Class, Owner, Index: Integer): Boolean;
  246.         var
  247.             thePeek: WindowPeek;
  248.             WData: WDataHandle;
  249.             OkSoFar: Boolean;
  250.             Result: Integer;
  251.     begin
  252.         OkSoFar := true;
  253.         thePeek := WindowPeek(WhichWIndow);
  254.         WData := WDataHandle(NewHandle(SizeOf(WindowData)));
  255.         Result := MemError;
  256.         if Result <> 0 then
  257.             begin
  258.                 OkSoFar := False;
  259.                 doOSErr(MemError);
  260.             end;
  261.  
  262.         if OkSoFar then
  263.             begin
  264.                 thePeek^.RefCon := LongInt(WData);
  265.                 WData^^.Class := Class;
  266.                 WData^^.Owner := Owner;
  267.                 WData^^.Index := Index;
  268.             end;
  269.  
  270.         InitializeWindow := OkSoFar;
  271.     end;
  272.  
  273.  
  274.     procedure DisposeWData (whichWindow: WindowPtr);
  275.         var
  276.             WData: WDataHandle;
  277.     begin
  278.         WData := GetWData(whichWindow);
  279.         if IsValidWindowHandle(WData) then
  280.             DisposHandle(Handle(WData))
  281.         else
  282.             doOSErr(BadWindow);
  283.     end;
  284.  
  285.  
  286. {I hope this is still a kosher way to find out which window is really in the front!  If this is no longer}
  287. {kosher, then I'd just make an invisible window which is always frontmost (a la BringToFront) and}
  288. {use it as my starting point for the search through the linked list.}
  289.     function findFrontWindow: WindowPtr;
  290.         const
  291.             WindowList = $9D6;
  292.         type
  293.             PtrPtr = ^Ptr;
  294.         var
  295.             WindowListPtr: PtrPtr;
  296.     begin
  297.         WindowListPtr := Pointer(WindowList);
  298.         findFrontWindow := WindowPtr(WindowListPtr^);  {was: FrontWindow}
  299.     end;
  300.  
  301.  
  302.     function SearchWindow (Class, Owner, Index: Integer): WindowPtr;
  303.         var
  304.             done: boolean;
  305.             ThisWindow, FoundWindow: WindowPtr;
  306.             WData: WDataHandle;
  307.             ThisWPeek: WindowPeek;
  308.     begin
  309.         done := false;
  310.         FoundWindow := nil;
  311.         ThisWindow := FindFrontWindow;
  312.         if ThisWindow <> nil then
  313.             while not done do
  314.                 begin
  315.                     WData := GetWData(ThisWindow);
  316.                     if IsValidWindowHandle(WData) then
  317.                         if WData^^.Class = Class then
  318.                             if WData^^.Owner = Owner then
  319.                                 if WData^^.Index = Index then
  320.                                     begin
  321.                                         FoundWindow := ThisWindow;
  322.                                         Done := true;
  323.                                     end;
  324.                     if not Done then
  325.                         begin
  326.                             ThisWPeek := WindowPeek(ThisWindow);
  327.                             ThisWPeek := ThisWPeek^.nextWindow;
  328.                             ThisWindow := WindowPtr(ThisWPeek);
  329.                             if ThisWindow = nil then
  330.                                 Done := true;
  331.                         end;
  332.                 end;
  333.         SearchWindow := FoundWindow;
  334.     end;
  335.  
  336.  
  337.     function CantActivate (Class, Owner, Index: Integer): Boolean;
  338.         var
  339.             ThisWindow: WindowPtr;
  340.     begin
  341.         CantActivate := TRUE;  {Assume the window doesn't exist.}
  342.         ThisWindow := SearchWindow(Class, Owner, Index);
  343.         if ThisWindow <> nil then
  344.             begin
  345.                 SelectWindow(ThisWindow);
  346.                 CantActivate := FALSE;  {The window exists, so we _can_ activate it.}
  347.             end;
  348.     end;
  349.  
  350.     function FindUniqueWindow (Class: Integer): WindowPtr;
  351.     begin
  352.         FindUniqueWindow := SearchWindow(Class, 0, 0);
  353. {By convention, all unique windows have 0 and 0 as their other arguments.}
  354.     end;
  355.  
  356.     procedure AutoPositionPalettes (WhichWindow: WindowPtr);
  357.     begin
  358. {If you want your palettes to be automatically positioned next to a certain kind of window, put code here}
  359. {to move the palettes to the right position.}
  360.     end;
  361.  
  362. {This internal routine is used to hilite or unhilite windows.  Note that there's more going on here than}
  363. {just drawing the hiliting on the window.  For our purposes, 'hilite' means 'bring it to the front, hilite it,}
  364. {and display its palettes as well'.  'Unhilite' means 'Turn off its hiliting, and hide its palettes also.'}
  365.     procedure SetPrivateHilite (whichWindow: WindowPtr; Hilite: Boolean);
  366.     begin
  367.         if WhichWindow = nil then
  368.             doOSErr(BadWindow);
  369.         if Hilite then
  370.             begin
  371.                 BringToFront(whichWindow);
  372.                 ShowHide(whichWindow, TRUE);
  373.             end;
  374.         HiliteWindow(whichWindow, Hilite);
  375. {If this window has a palette, bring it to the front as well.}
  376.         case GetWClass(whichWindow) of
  377. {You should modify the case selectors here to handle whatever kinds of windows with palettes your application supports.}
  378.             wMiscWindowWithPalette: 
  379.                 begin
  380.                     if Hilite then
  381.                         begin
  382.                             AutoPositionPalettes(whichWindow);
  383.                             BringToFront(FindUniqueWindow(wMiscPalette));
  384.                         end;
  385.                     ShowHide(FindUniqueWindow(wMiscPalette), Hilite);
  386.                 end;
  387.             otherwise
  388. {If this window owns no palettes, do nothing.}
  389.         end;
  390.     end;
  391.  
  392. {Since we're using windows in a slightly unorthodox way, we can't rely on the Activate and Deactivate events}
  393. {to do our work for us.  doSendPrivateActivateMessage is the routine to handle our equivalent of an Activate}
  394. {event for the PrivateFrontWindow.  (Palettes can never be the PrivateFrontWindow, by definition.)}
  395.     procedure doSendPrivateActivateMessage (whichWindow: WindowPtr);
  396.     external;
  397.  
  398.     procedure privateActivate (whichWindow: WindowPtr);
  399.     begin
  400.         SetPrivateHilite(PrivateFrontWindow, FALSE);
  401.         PrivateFrontWindow := whichWindow;
  402.         SetPrivateHilite(PrivateFrontWindow, TRUE);
  403. {Then send a message to update menus, etc.}
  404.         doSendPrivateActivateMessage(PrivateFrontWindow);
  405.     end;
  406.  
  407. end.
  408.